/*NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

	Copyright (c) 2009 Jounayd Id Salah.
	All rights reserved.

	This file is part of Blacklib.

	Blacklib is free software: you can redistribute it and/or modify it under the 
	terms of the GNU Lesser General Public License as published by the Free Software 
	Foundation, either version 3 of the License, or (at your option) any later version.
	Blacklib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
	PURPOSE.  See the GNU Lesser General Public License for more details.
	You should have received a copy of the GNU Lesser General Public License along with 
	Blacklib.  If not, see <http://www.gnu.org/licenses/>.
*/
//NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

#include <math.h>
#include <emmintrin.h>
#include "maths/valid.h"
#include "maths/types.h"

#ifdef bBIG_ENDIAN
# define bIEXP		0
# define bIMAN		1
#else
# define bIEXP		1
# define bIMAN		0
#endif //bBIG_ENDIAN

namespace black
{

	const double D_DOUBLE_MAGIC					= double (6755399441055744.0); 	    //2^52 * 1.5, uses limited precision to floor
	const double D_DOUBLE_MAGIC_DELTA      		= (1.5e-8);                         //almost .5f = .5f + 1e^(number of exp bit)
	const double D_DOUBLE_MAGIC_ROUND_EPSILON	= (.5f - D_DOUBLE_MAGIC_DELTA);     //almost .5f = .5f - 1e^(number of exp bit)

	//__________________________________________________________________________
	float ceil ( const float x )					{ return ::ceilf( x ); }

	//__________________________________________________________________________
	int ceilToInt( const float x )
	{
		return ceilToInt( (double)x );
	}
	int ceilToInt( const double x )
	{
		bCOPYRIGHT( "Sree Kotay", "Stereopsis", "Stereopsis.com" );
		{
			return roundToInt( x + D_DOUBLE_MAGIC_ROUND_EPSILON );
		}
	}

	//__________________________________________________________________________
	float clamp ( const float x, const float fMin, const float fMax )
	{
		bLIGHT_ASSERT( fMin <= fMax );
		return x < fMin ? fMin : ( x > fMax ? fMax : x );
	}

	//__________________________________________________________________________
	void	copySign	( float x, float& res )
	{
		bCOPYRIGHT( "Sree Kotay", "Stereopsis", "Stereopsis.com" );
		{
			((int*)&res)[ bIEXP ] &= ~(((int*)&x)[ bIEXP ]&0x80000000);
		}
	}
	void	copySign	( double x, double& res )
	{
		bCOPYRIGHT( "Sree Kotay", "Stereopsis", "Stereopsis.com" );
		{
			((int*)&res)[ bIEXP ] &= ~(((int*)&x)[ bIEXP ]&0x80000000);
		}
	}

	//__________________________________________________________________________
	float floor ( const float x )					{ return ::floor( x ); }

	//__________________________________________________________________________
	int floorToInt( const float x )
	{
		floorToInt( (double) x );
	}
	int floorToInt( const double x )
	{
		bCOPYRIGHT( "Sree Kotay", "Stereopsis", "Stereopsis.com" );
		{
			return roundToInt( x - D_DOUBLE_MAGIC_ROUND_EPSILON );
		}
	}

	//__________________________________________________________________________
	float truncate ( const float x )
	{
		return toFloat( roundToInt(x) );
		/*static const float fMagicValue = 1.5f * pow( 2, 24 );
		x += fMagicValue;
		return x - fMagicValue;*/
	}

	//__________________________________________________________________________
	float round ( float x )
	{
#ifdef bAUTO_SSE
		return (float)( roundToInt( x > 0.f ? x + .5f : x - .5f ) );
#else
		bNEEDS_TESTS;
		bCOPYRIGHT( "Yossarian King", "Electronic Arts Canada", "Game Programming gems 2" )
		{
			static const long	iBias = ((23 + 127) << 23) + (1 << 22);
			static const float	fBias = *( (float*)( &iBias ) );

			data32 d32;
			d32.f = x + fBias;
			d32.i -= iBias;

			return toFloat( d32.i );
		}
#endif
		/*static const float fMagic = pow( 2, 23 ) * 1.5f;
		float x2		= x + fMagic; //2^52 * 1.5,  uses limited precision to floor
		return ((int*)&x2)[ bIMAN ];*/
	}

	//__________________________________________________________________________
	float roundPos ( const float x )
	{
		bLIGHT_ASSERT( x >= 0.f );
		return toFloat( roundToInt( x + .5f ) );
	}

	//__________________________________________________________________________
	float exp ( const float x )						{ return ::expf( x ); }

	//__________________________________________________________________________
	float pow ( const float x, const float y )		{ return ::pow( x, y ); }

	//__________________________________________________________________________
	float pow ( const float x, const int y )		{ return ::pow( x, y ); }

	//__________________________________________________________________________
	float log ( const float x )						{ return ::log( x ); }

	//__________________________________________________________________________
	float sqrt ( const float x )					{ return ::sqrt( x ); }

	//__________________________________________________________________________
	int abs ( const int x )
	{
#ifdef bMINIMIZE_BRANCHING
		register int tmp = x >> 31;
		return (x ^ tmp) - tmp;
#else
		return x > 0.f ? x : -x;
#endif
	}
	float abs ( const float x )
	{
#if defined(bAUTO_SSE)
		return x < 0.f ? -x : x;
#else
		data32 tmp;
		tmp.f = x;
		tmp.i &= 0x7fffffff;
		return tmp.f;
#endif
	}
	double abs ( const double x )
	{
#if defined( bAUTO_SSE )
		return x < 0.f ? -x : x;
#else
		return ::abs( x );
#endif
		/*const int tmp = *( (int*)&x) & 0x7fffffffffffffff;
		return *(double*)( &tmp );*/
	}
	__m128 abs ( const __m128 x )
	{
		static const __m128 mmZero = _mm_setzero_ps();
		return _mm_max_ps( mmZero, x );
	}

	//__________________________________________________________________________
	int	roundToInt ( float x )
	{
		return roundToInt( (double) x );

		/*return (val<0) ?   xs_CRoundToInt(val-dme) : 
			xs_CRoundToInt(val+dme);*/
//#ifdef bAUTO_SSE
		//return (int)x;
/*#else
		//it rounds instead of truncating

		bNEEDS_TESTS;
		bCOPYRIGHT( "Yossarian King", "Electronic Arts Canada", "Game Programming gems 2" )
		{
			static const long	iBias = ((23 + 127) << 23) + (1 << 22);
			static const float	fBias = *( (float*)( &iBias ) ) - 0.5f;

			data32 d32;
			d32.f = x + fBias;
			d32.i -= iBias;

			return d32.i;
		}
#endif*/
	}
	int	roundToInt ( double x )
	{
		bCOPYRIGHT( "Sree Kotay", "Stereopsis", "Stereopsis.com" );
		{
			x = x + D_DOUBLE_MAGIC; //2^52 * 1.5,  uses limited precision to floor
			return ((int*)&x)[ bIMAN ];
		}
	}

	//__________________________________________________________________________
	int toInt( const float x )
	{
		return (int) x;
		/*return toInt( (double) x );*/
	}
	int toInt( const double x )
	{
		return (int) x;
		/*
		bCOPYRIGHT( "Sree Kotay", "Stereopsis", "Stereopsis.com" );
		{
			return (x < 0) ? ceilToInt( x ) : floorToInt( x );
		}
		*/
	}

	//__________________________________________________________________________
	float toFloat( const int i )
	{
		return (float)i;
	}

	//__________________________________________________________________________
	template <typename T> T min( const T& a, const T& b )
	{
		return a < b ? a : b;
	}
	template <typename T> T min( const T& a, const T& b, const T& c )
	{
		return a < b ? (a < c ? a : c) : (b < c ? b : c);
	}

	//__________________________________________________________________________
	int mod ( const int a, const int mult )			{ return a%mult; }
	uint mod ( const uint a, const uint mult )		{ return a%mult; }

	//__________________________________________________________________________
	template <typename T> T max( const T& a, const T& b )
	{
		return a > b ? a : b;
	}
	template <typename T> T max( const T& a, const T& b, const T& c )
	{
		return a > b ? (a > c ? a : c) : (b > c ? b : c);
	}

	//__________________________________________________________________________
	template <typename T>
	void swap ( T& a, T& b ) { register const T tmp = a; a = b; b = tmp; }

	//__________________________________________________________________________
	float lerp ( const float from, const float to, const float coef )
	{
		bLIGHT_ASSERT( coef >= 0.f );
		bLIGHT_ASSERT( coef <= 1.f );
		return to + coef * (from - to);
	}

	//__________________________________________________________________________
	float fastInvSqrt( float x )
	{
		bLIGHT_ASSERT( x > 0.f );
		const float x2 = x * 0.5f;
		long i  = * (long *) &x;
		i		= bF_FASTINVSQRT_CONSTANT - (i >> 1);
		x		= * (float *) &i;
		x		= x * (1.5f - (x2 * x * x)); // 1st newton iteration

		return x;
	}

	//__________________________________________________________________________
	bool equals	( const float x, const float y, const float fRelativeTolerance, const float fAbsoluteTolerance )
	{
		if ( fRelativeTolerance == 0.f && fAbsoluteTolerance == 0.f )
		{
			return x == y;
		}
		else if ( fAbsoluteTolerance == 0.f )
		{
			return (abs(x - y) <= fRelativeTolerance * max(1.0f, abs(x), abs(y)));
		}
		else if ( fRelativeTolerance == 0.f )
		{
			return abs(x - y) <= fAbsoluteTolerance;
		}
		else
		{
			return (abs(x - y) <= max(fAbsoluteTolerance, fRelativeTolerance * max(abs(x), abs(y))));
		}
	}

	//__________________________________________________________________________
	/*! coming from 'jj's useful and ugly BIT WIZARDRY page' */
	uint absdiff( uint a, uint b )
	{
		bLIGHT_ASSERT( ( a & 0x80000000 ) == 0 );
		bLIGHT_ASSERT( ( b & 0x80000000 ) == 0 );
		uint	d1 = b - a;
		uint	d2 = (d1 & (d1 >> 31)) << 1;
		return  d1 - d2; // == (b - d) - (a + d);
	}
}
